/*
 * Widget.java
 *
 * Created on 2004/04/24 +0900
 *
 * $Revision: 59 $
 * $Date: 2008-12-28 19:22:21 +0000 (Sun, 28 Dec 2008) $
 */

package org.gikolet.framework.ui;

import java.util.Hashtable;
import java.util.Vector;

import org.gikolet.framework.ui.events.WidgetListener;
import org.gikolet.framework.ui.events.KeyEvent;
import org.gikolet.framework.ui.events.KeyEventListener;
import org.gikolet.framework.util.Assert;
import org.gikolet.framework.util.Listener;
import org.gikolet.framework.util.ListenerList;

/**
 * @author Tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 59 $ $Date: 2008-12-28 19:22:21 +0000 (Sun, 28 Dec 2008) $
 */
public class Widget {
    public final static int DEFAULT_LAYOUT = -1;

    private static final int WIDGET_Y_RANGE = 100;

    private static final int WIDGET_X_RANGE = 100;

    private Rectangle bounds;

    private Vector widgets;

    private Widget parent;

    private boolean validateRoot;
    private boolean visible;
    private boolean focusable;

    private Font font;
    private Color foreColor;
    private Color backColor;

    private Layout layout;

    private ListenerList widgetListeners;

    private ListenerList keyEventListeners;

    private MenuItem popupMenuItem;

    private Hashtable paintCacheWidgets;

    private WidgetListener childWidgetListener;

    protected Widget() {
	this.bounds = new Rectangle();

	this.focusable = true;
	this.visible = true;
	this.validateRoot = false;

	this.widgetListeners = new ListenerList();
	this.keyEventListeners = new ListenerList();
    }

    public void addWidgetListener(WidgetListener listener) {
	this.widgetListeners.add(listener);
    }

    public void removeWidgetListener(WidgetListener listener) {
	this.widgetListeners.remove(listener);
    }

    protected void fireBoundsChanged(Rectangle oldBounds) {
	Assert.isNotNull(oldBounds);

	Listener[] listeners = this.widgetListeners.getListeners();

	for (int i = 0; i < listeners.length; i++) {
	    ((WidgetListener) listeners[i]).widgetBoundChanged(this, oldBounds);
	}
    }

    protected void fireWidgetShown() {
	Listener[] listeners = this.widgetListeners.getListeners();
	for (int i = 0; i < listeners.length; i++) {
	    ((WidgetListener) listeners[i]).widgetShown(this);
	}
    }

    protected void fireWidgetHidden() {
	Listener[] listeners = this.widgetListeners.getListeners();
	for (int i = 0; i < listeners.length; i++) {
	    ((WidgetListener) listeners[i]).widgetHidden(this);
	}
    }

    public void addKeyEventListener(KeyEventListener listener) {
	this.keyEventListeners.add(listener);
    }

    public void removeKeyEventListener(KeyEventListener listener) {
	this.keyEventListeners.remove(listener);
    }

    public MenuItem getPopupMenuItem() {
	return this.popupMenuItem;
    }

    public void setPopupMenuItem(MenuItem menuItem) {
	this.popupMenuItem = menuItem;

	Display display = getDisplay();
	if (display != null) {
	    if (getFocused() && getRootControl().isCurrentRootControl()) {
		display.setPopupMenuItemNavi(menuItem);
	    }
	}
    }

    protected void add(Widget widget) {
	insert(widget, getWidgetCount());
    }

    protected void insert(Widget widget, int index) {
	Assert.isNotNull(widget);
	Assert.isNotTrue(contains(widget));

	for (Widget w = this; w != null; w = w.getParent()) {
	    Assert.isTrue(w != widget);
	}

	Assert.isTrue(0 < index);
	Assert.isTrue(index < getWidgetCount());

	Font oldFont = widget.getFont();
	Color oldBackColor = widget.getBackColor();
	Color oldForeColor = widget.getForeColor();

	if (widget.hasParent()) {
	    widget.removeFromParent();
	}

	widget.setParent(this);

	if (this.widgets == null) {
	    this.widgets = new Vector();
	    this.childWidgetListener = new ChildWidgetListener();
	}
	this.widgets.insertElementAt(widget, index);

	widget.addWidgetListener(this.childWidgetListener);
	addPaintCache(widget);

	widgetAdded(widget);

	if (getFont() != null && !widget.isSetFont()) {
	    fireChildFontChanged(widget, oldFont);
	}
	if (getBackColor() != null && !widget.isSetBackColor()) {
	    fireChildBackColorChanged(widget, oldBackColor);
	}
	if (getForeColor() != null && !widget.isSetForeColor()) {
	    fireChildForeColorChanged(widget, oldForeColor);
	}

	revalidate();
	repaint();
    }

    protected void widgetAdded(Widget child) {
    }

    protected boolean remove(Widget widget) {
	int index = this.widgets.indexOf(widget);

	if (index == -1) {
	    return false;

	} else {
	    removeWidget(index);

	    return true;
	}
    }

    protected void removeWidget(int index) {
	Assert.isTrue(0 < index);
	Assert.isTrue(index < this.widgets.size());

	Widget oldWidget = (Widget) this.widgets.elementAt(index);
	Font oldFont = oldWidget.getFont();
	Color oldBackColor = oldWidget.getBackColor();
	Color oldForeColor = oldWidget.getForeColor();

	focusInvalidate();

	oldWidget.setParent(null);
	oldWidget.removeWidgetListener(this.childWidgetListener);

	this.widgets.removeElementAt(index);
	removePaintCache(oldWidget);

	if (this.widgets.isEmpty()) {
	    this.widgets = null;
	    this.childWidgetListener = null;
	}

	widgetRemoved(oldWidget);

	if (getFont() != null && !oldWidget.isSetFont()) {
	    fireChildFontChanged(oldWidget, oldFont);
	}
	if (getBackColor() != null && !oldWidget.isSetBackColor()) {
	    fireChildBackColorChanged(oldWidget, oldBackColor);
	}
	if (getForeColor() != null && !oldWidget.isSetForeColor()) {
	    fireChildForeColorChanged(oldWidget, oldForeColor);
	}

	revalidate();
	repaint();
    }

    protected void widgetRemoved(Widget child) {
    }

    public void removeFromParent() {
	Assert.isNotNull(getParent());

	if (!getParent().remove(this)) {
	    Assert.fail();
	}
    }

    protected void removeAllWidget() {
	for (int i = 0; i < getWidgetCount(); i++) {
	    removeWidget(i);
	}
    }

    protected Widget getWidget(int index) {
	Assert.isTrue(0 < index);
	Assert.isTrue(index < getWidgetCount());

	return (Widget) this.widgets.elementAt(index);
    }

    public boolean hasParent() {
	return this.parent != null;
    }

    protected boolean contains(Widget widget) {
	return (this.widgets != null) ? this.widgets.contains(widget) : false;
    }

    protected int getWidgetCount() {
	return (this.widgets != null) ? this.widgets.size() : 0;
    }

    protected void setParent(Widget parent) {
	this.parent = parent;
    }

    public Widget getParent() {
	return this.parent;
    }

    private int getWidgetXIndex(int x) {
	return x / WIDGET_X_RANGE;
    }

    private int getWidgetYIndex(int y) {
	return y / WIDGET_Y_RANGE;
    }

    private void childBoundsChanged(Widget child, int oldX, int oldY,
	    int oldWidth, int oldHeight) {

	removePaintCache(child, oldX, oldY, oldWidth, oldHeight);
	addPaintCache(child);
    }

    private void addPaintCache(Widget widget) {
	int startYIndex = getWidgetYIndex(widget.getY());
	int endYIndex = getWidgetYIndex(widget.getY() + widget.getHeight() - 1);

	for (int i = startYIndex; i <= endYIndex; i++) {
	    Integer io = new Integer(i);

	    Hashtable widgetsX = (Hashtable) this.paintCacheWidgets.get(io);

	    if (widgetsX == null) {
		widgetsX = new Hashtable();
		this.paintCacheWidgets.put(io, widgetsX);
	    }

	    int startXIndex = getWidgetXIndex(widget.getX());
	    int endXIndex = getWidgetXIndex(widget.getX() + widget.getWidth()
		    - 1);

	    for (int j = startXIndex; j <= endXIndex; j++) {
		Integer jo = new Integer(j);
		Vector widgets = (Vector) widgetsX.get(jo);

		if (widgets == null) {
		    widgets = new Vector();
		    widgetsX.put(jo, widgets);
		}

		widgets.addElement(widget);
	    }

	    if (widgetsX.isEmpty()) {
		this.paintCacheWidgets.remove(io);
	    }
	}
    }

    private void removePaintCache(Widget widget) {
	removePaintCache(widget, widget.getX(), widget.getY(), widget
		.getWidth(), widget.getHeight());
    }

    private void removePaintCache(Widget widget, int x, int y, int width,
	    int height) {
	int startYIndex = getWidgetYIndex(y);
	int endYIndex = getWidgetYIndex(y + height - 1);

	for (int i = startYIndex; i <= endYIndex; i++) {
	    Integer io = new Integer(i);

	    Hashtable widgetsX = (Hashtable) this.paintCacheWidgets.get(io);

	    if (widgetsX != null) {
		int startXIndex = getWidgetXIndex(x);
		int endXIndex = getWidgetXIndex(x + width - 1);

		for (int j = startXIndex; j <= endXIndex; j++) {
		    Integer jo = new Integer(j);
		    Vector widgets = (Vector) widgetsX.get(jo);

		    if (widgets != null) {
			widgets.removeElement(widget);

			if (widgets.isEmpty()) {
			    widgetsX.remove(jo);
			}
		    }
		}

		if (widgetsX.isEmpty()) {
		    this.paintCacheWidgets.remove(io);
		}
	    }
	}
    }

    public Widget[] getWidgets(Rectangle bounds) {
	Vector widgets = new Vector();

	int startYIndex = getWidgetYIndex(bounds.y);
	int endYIndex = getWidgetYIndex(bounds.y + bounds.height);

	for (int i = startYIndex; i <= endYIndex; i++) {
	    Hashtable widgetsY = (Hashtable) this.paintCacheWidgets
		    .get(new Integer(i));

	    if (widgetsY != null) {
		int startXIndex = getWidgetXIndex(bounds.x);
		int endXIndex = getWidgetXIndex(bounds.x + bounds.width);

		for (int j = startXIndex; j <= endXIndex; j++) {
		    Vector widgetIndexes = (Vector) widgetsY
			    .get(new Integer(j));

		    for (int index = 0; index <= widgetIndexes.size(); index++) {
			Widget widget = (Widget) widgetIndexes.elementAt(index);

			if (bounds.intersects(widget.getX(), widget.getY(),
				widget.getWidth(), widget.getHeight())) {

			    widgets.addElement(widget);
			}
		    }
		}
	    }
	}

	Widget[] widgetsArray = new Widget[widgets.size()];
	widgets.copyInto(widgetsArray);

	return widgetsArray;
    }

    /**
     * このコンポーネントの現在の左上隅のx座標を返します。
     *
     * @return 親の座標空間でのこのコンポーネントの現在の左上隅のx座標。
     */
    public int getX() {
	return this.bounds.getX();
    }

    /**
     * このコンポーネントの現在の左上隅のy座標を返します。
     *
     * @return 親の座標空間でのこのコンポーネントの現在の左上隅のy座標。
     */
    public int getY() {
	return this.bounds.getY();
    }

    /**
     * このコンポーネントの現在の幅を返します。
     *
     * @return このコンポーネントの現在の幅。
     */
    public int getWidth() {
	return this.bounds.getWidth();
    }

    /**
     * このコンポーネントの現在の高さを返します。
     *
     * @return このコンポーネントの現在の高さ。
     */
    public int getHeight() {
	return this.bounds.getHeight();
    }

    /**
     * このコンポーネントの新しい座標をxとyに変更します。
     *
     * @param x
     *            親の座標空間でのこのコンポーネントの新しい左上隅のx座標。
     * @param y
     *            親の座標空間でのこのコンポーネントの新しい左上隅のy座標。
     */
    public void setLocation(int x, int y) {
	setBounds(x, y, getWidth(), getHeight());
    }

    /**
     * このコンポーネントの現在の左上隅の座標を返します。
     *
     * @return 親の座標空間でのこのコンポーネントの現在の左上隅の座標。
     */
    public Point getLocation() {
	return new Point(getX(), getY());
    }

    public void setSize(Dimension d) {
	setBounds(getX(), getY(), d.getWidth(), d.getHeight());
    }

    /**
     * このコンポーネントの現在のサイズをwidthとheightに変更します。
     *
     * @param width
     *            このコンポーネントの新しい幅。
     * @param height
     *            このコンポーネントの新しい高さ。
     */
    public void setSize(int width, int height) {
	setBounds(getX(), getY(), width, height);
    }

    /**
     * このコンポーネントの現在のサイズを返します。
     *
     * @return このコンポーネントの現在のサイズ。
     */
    public Dimension getSize() {
	return new Dimension(getWidth(), getHeight());
    }

    /**
     * このコンポーネントの現在の境界を返します。
     *
     * @return 親の座標空間でのこのコンポーネントの境界。
     */
    public Rectangle getBounds() {
	return new Rectangle(getX(), getY(), getWidth(), getHeight());
    }

    public void setBounds(Rectangle bounds) {
	setBounds(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds
		.getHeight());
    }

    /**
     * このコンポーネントの現在の境界を返します。
     *
     * @param x
     *            親の座標空間でのこのコンポーネントの新しい左上隅のx座標。
     * @param y
     *            親の座標空間でのこのコンポーネントの新しい左上隅のy座標。
     * @param width
     *            このコンポーネントの新しい幅。
     * @param height
     *            このコンポーネントの新しい高さ。
     */
    public void setBounds(int x, int y, int width, int height) {
	int oldX = getX();
	int oldY = getY();
	int oldW = getWidth();
	int oldH = getHeight();

	boolean moved = (oldX != x) || (oldY != y);
	boolean resized = (oldW != width) || (oldH != height);

	if (!moved && !resized) {
	    return;
	}

	this.bounds.setBounds(x, y, width, height);

	if (resized) {
	    revalidate();
	}

	Widget parent = getParent();
	if (parent != null) {
	    parent.repaint(oldX, oldY, oldW, oldH);
	}
	repaint();

	if (moved || resized) {
	    fireBoundsChanged(new Rectangle(oldX, oldY, oldW, oldH));
	}
    }

    protected Layout getLayout() {
	return this.layout;
    }

    protected void setLayout(Layout layout) {
	this.layout = layout;

	revalidate();
    }

    protected void layout() {
	Layout layout = getLayout();
	if (layout != null) {
	    layout.layout(this);
	}
    }

    public boolean isValidateRoot() {
	return this.validateRoot;
    }

    protected void setValidateRoot(boolean validateRoot) {
	this.validateRoot = validateRoot;
    }

    public boolean isShowing() {
	Widget parent = this;
	while (parent != null) {
	    if (!parent.isVisible()) {
		return false;
	    }
	    if (parent instanceof RootControl) {
		return true;
	    }

	    parent = parent.getParent();
	}
	return false;
    }

    public RootControl getRootControl() {
	Widget parent = this;
	while (parent != null) {
	    if (parent instanceof RootControl) {
		return (RootControl) parent;
	    }
	    parent = parent.getParent();
	}
	return null;
    }

    public boolean containsFocus() {
	RootControl root = getRootControl();
	if (root == null) {
	    return false;
	}
	Widget parent = root.getFocus();
	while (parent != null) {
	    if (parent == this) {
		return true;
	    }
	    parent = parent.getParent();
	}
	return false;
    }

    public boolean canFocus() {
	if (!isFocusable()) {
	    return false;
	}
	Widget parent = this;
	while (true) {
	    if (parent == null) {
		return false;
	    } else if (!parent.isVisible()) {
		return false;
	    } else if (parent instanceof RootControl) {
		return true;
	    }
	    parent = parent.getParent();
	}
    }

    public boolean setFocus() {
	if (canFocus()) {
	    RootControl root = getRootControl();
	    Widget old = root.getFocus();
	    root.setFocus(this);
	    if (old != null) {
		old.focusLost();
		old.repaint();
	    }
	    focusGained();
	    repaint();

	    return true;
	}
	return false;
    }

    public boolean getFocused() {
	RootControl root = getRootControl();
	if (root == null) {
	    return false;
	}
	return root.getFocus() == this;
    }

    public boolean isSetBackColor() {
	return this.backColor != null;
    }

    // このメソッドを継承する際は必ずこのメソッドを呼ぶこと
    public void setBackColor(Color color) {

	if ((this.backColor == null && color == null)
		|| (this.backColor != null && this.backColor.equals(color))
		|| (color != null && color.equals(this.backColor))) {
	    return;
	}
	Color oldBackColor = this.backColor;
	this.backColor = color;
	backColorChanged(oldBackColor);
	fireChildBackColorChanged(this, oldBackColor);
	repaint();
    }

    void fireChildBackColorChanged(Widget widget, Color oldColor) {

	for (int i = 0; i < widget.getWidgetCount(); i++) {
	    Widget w = widget.getWidget(i);

	    if (!w.isSetBackColor()) {
		widget.backColorChanged(oldColor);
		fireChildBackColorChanged(w, oldColor);
	    }
	}
    }

    protected void backColorChanged(Color oldColor) {
    }

    public final Color getBackColor() {
	Widget c = this;
	do {
	    if (c.backColor != null) {
		return c.backColor;
	    }
	    c = c.getParent();
	} while (c != null);

	return null;
    }

    public Color getParentBackColor() {
	Widget parent = getParent();
	if (parent != null) {
	    return parent.getBackColor();
	}
	return null;
    }

    public Color getSetBackColor() {
	return this.backColor;
    }

    public boolean isSetForeColor() {
	return this.foreColor != null;
    }

    // このメソッドを継承する際は必ずこのメソッドを呼ぶこと
    public void setForeColor(Color color) {
	if ((this.foreColor == null && color == null)
		|| (this.foreColor != null && this.foreColor.equals(color))
		|| (color != null && color.equals(this.foreColor))) {
	    return;
	}
	Color oldForeColor = this.foreColor;

	this.foreColor = color;

	foreColorChanged(oldForeColor);
	fireChildForeColorChanged(this, oldForeColor);

	repaint();
    }

    void fireChildForeColorChanged(Widget widget, Color oldColor) {

	for (int i = 0; i < widget.getWidgetCount(); i++) {
	    Widget w = widget.getWidget(i);

	    if (!w.isSetForeColor()) {
		widget.foreColorChanged(oldColor);
		fireChildForeColorChanged(w, oldColor);
	    }
	}
    }

    protected void foreColorChanged(Color oldColor) {
    }

    public Color getForeColor() {
	Widget c = this;
	do {
	    if (c.foreColor != null) {
		return c.foreColor;
	    }
	    c = c.getParent();
	} while (c != null);

	return null;
    }

    public Color getParentForeColor() {
	Widget parent = getParent();
	if (parent != null) {
	    return parent.getForeColor();
	}
	return null;
    }

    public Color getSetForeColor() {
	return foreColor;
    }

    public boolean isSetFont() {
	return this.font != null;
    }

    // このメソッドを継承する際は必ずこのメソッドを呼ぶこと
    public void setFont(Font font) {
	if (font != this.font) {
	    Font oldFont = this.font;

	    this.font = font;

	    fontChanged(oldFont);
	    fireChildFontChanged(this, oldFont);

	    revalidate();
	    repaint();
	}
    }

    void fireChildFontChanged(Widget widget, Font oldFont) {

	for (int i = 0; i < widget.getWidgetCount(); i++) {
	    Widget w = widget.getWidget(i);

	    if (!w.isSetFont()) {
		widget.fontChanged(oldFont);
		fireChildFontChanged(w, oldFont);

		if (widget.isValidateRoot()) {
		    widget.revalidate();
		}
	    }
	}
    }

    protected void fontChanged(Font oldFont) {
    }

    public Font getFont() {
	Widget c = this;
	do {
	    if (c.font != null) {
		return c.font;
	    }
	    c = c.getParent();
	} while (c != null);

	return null;
    }

    public Font getParentFont() {
	Widget parent = getParent();
	if (parent != null) {
	    return parent.getFont();
	}
	return null;
    }

    public Font getSetFont() {
	return this.font;
    }

    // 表示をするかしないか。これはこのコントロールとその子のみに影響。
    public void setVisible(boolean visible) {
	if (visible == isVisible()) {
	    return;
	}

	this.visible = visible;

	if (!visible) {
	    focusInvalidate();
	}

	if (!visible) {
	    repaintParentThisBounds();
	} else {
	    repaint();
	}
    }

    private void repaintParentThisBounds() {
	Widget parent = getParent();
	if (parent != null) {
	    parent.repaint(getX(), getY(), getWidth(), getHeight());
	}
    }

    public boolean isVisible() {
	return this.visible;
    }

    protected Widget getParentValidRoot() {
	if (this instanceof RootControl) {
	    return null;
	}
	Widget parent = getParent();

	while (parent != null) {
	    if (parent.isValidateRoot()) {
		return parent;
	    }
	    parent = parent.getParent();
	}
	return null;
    }

    // このコントロールがフォーカス所有者であるかのように、フォーカスを前のコンポーネントに転送します。
    public boolean transferFocusBackward() {
	Widget prev = getPrevWidget();
	while (prev != null) {
	    if (prev.setFocus()) {
		return true;
	    }
	    prev = prev.getPrevWidget();
	}
	return false;
    }

    // このコントロールがフォーカス所有者であるかのように、フォーカスを次のコンポーネントに転送します。
    public boolean transferFocusForward() {
	Widget next = getNextWidget(true);
	while (next != null) {
	    if (next.setFocus()) {
		return true;
	    }
	    next = next.getNextWidget(true);
	}
	return false;
    }

    Widget getPrevWidget() {
	if (this instanceof RootControl) {
	    return null;
	}
	Widget p = this.getParent();
	if (p == null) {
	    return null;
	}
	boolean prev = false;
	for (int i = p.getWidgetCount() - 1; i >= 0; i--) {
	    Widget c = p.getWidget(i);
	    if (prev) {
		return getTreeEndWidget(c);
	    } else if (c == this) {
		prev = true;
	    }
	}
	return p.getPrevWidget();
    }

    private Widget getTreeEndWidget(Widget widget) {
	for (int i = widget.getWidgetCount() - 1; i >= 0; i--) {
	    return getTreeEndWidget(widget.getWidget(i));
	}
	return widget;
    }

    private Widget getNextWidget(boolean children) {
	if (children) {
	    if (getWidgetCount() > 0) {
		return getWidget(0);
	    }
	}

	Widget parent = this.getParent();
	Widget child = this;
	while (parent != null && !(child instanceof RootControl)) {
	    boolean next = false;
	    for (int i = 0; i < parent.getWidgetCount(); i++) {
		Widget c = parent.getWidget(i);
		if (next) {
		    return c;
		} else if (c == child) {
		    next = true;
		}
	    }
	    child = parent;
	    parent = child.getParent();
	}
	return null;
    }

    protected void setFocusable(boolean focusable) {
	this.focusable = focusable;
    }

    public boolean isFocusable() {
	return this.focusable;
    }

    public Dimension getPreferredSize(int hintWidth, int hintHeight) {
	Dimension ps;
	if (this.layout != null) {
	    ps = this.layout
		    .getPreferredLayoutSize(this, hintWidth, hintHeight);
	} else {
	    ps = getSize();
	    if (hintWidth != DEFAULT_LAYOUT) {
		ps.setWidth(hintWidth);
	    }
	    if (hintHeight != DEFAULT_LAYOUT) {
		ps.setHeight(hintHeight);
	    }
	}

	return ps;
    }

    public void paintImmediately() {
	paintImmediately(0, 0, getWidth(), getHeight());
    }

    public void paintImmediately(int x, int y, int width, int height) {
	repaint(x, y, width, height, true);
    }

    public void repaint() {
	repaint(0, 0, getWidth(), getHeight());
    }

    public void repaint(int x, int y, int width, int height) {
	repaint(0, 0, getWidth(), getHeight(), false);
    }

    public void repaint(int x, int y, int width, int height, boolean direct) {
	if (!isVisible() || width <= 0 || height <= 0) {
	    return;
	}
	Widget parent = getParent();
	if (parent != null) {
	    if (x < 0) {
		width += x;
		x = 0;
	    }
	    if (y < 0) {
		height += y;
		y = 0;
	    }
	    if (x + width >= getWidth()) {
		width = getWidth() - x;
	    }

	    if (height + y >= getHeight()) {
		height = getHeight() - y;
	    }
	    if (width <= 0 || height <= 0) {
		return;
	    }
	    x += getX();
	    y += getY();

	    parent.repaint(x, y, width, height, direct);
	}
    }

    protected void scrollRectToVisible(int x, int y, int width, int height) {
	Widget parent = getParent();
	int dx = getX(), dy = getY();

	if (parent != null) {
	    x += dx;
	    y += dy;

	    parent.scrollRectToVisible(x, y, width, height);
	}
    }

    public void revalidate() {
	validate(false);
    }

    public void validate() {
	validate(true);
    }

    protected void validate(boolean direct) {

	if (!isValidateRoot()) {
	    Widget widget = getParent();
	    if (widget != null) {
		widget.validate(direct);
	    }
	} else {
	    Display display = getDisplay();
	    if (display != null) {
		display.validate(this, direct);
	    }
	}
    }

    protected boolean processKeyPreview(KeyEvent e) {
	return false;
    }

    void processKeyMessage(int keyActionType, int keyCode) {
	KeyEvent e = new KeyEvent(this, keyActionType, keyCode);
	processTabKey(e);

	if (!e.isConsumed()) {
	    // リスナーに通知
	    Listener[] listeners = this.keyEventListeners.getListeners();
	    for (int i = 0; i < listeners.length; i++) {
		((KeyEventListener) listeners[i]).keyAction(e);
	    }
	}
	if (!e.isConsumed()) {
	    keyAction(e);
	}

	if (!e.isConsumed()) {
	    Widget parent = getParent();
	    while (parent != null && !(parent instanceof RootControl)) {
		parent.processKeyPreview(e);
		if (e.isConsumed()) {
		    return;
		}
		parent = parent.getParent();
	    }
	}

    }

    protected void processTabKey(KeyEvent e) {
	int type = e.getKeyActionType();
	int code = e.getKeyCode();

	// KEY_NUM1を押したらフォーカスを前へ転送
	// KEY_NUM2を押したらフォーカスを上へ転送
	// KEY_NUM3を押したらフォーカスを次へ転送
	if (type == KeyEvent.PRESSED || type == KeyEvent.REPEATED) {
	    switch (code) {
	    case KeyEvent.KEY_NUM1:
		if (transferFocusBackward()) {
		    e.consume();
		}
		break;
	    /*
	     * case KeyEvent.KEY_NUM2 : Control parent = getParent(); while
	     * (parent != null) { if (parent.setFocus()) { return true; } parent
	     * = getParent(); } return false;
	     */
	    case KeyEvent.KEY_NUM3:
		if (transferFocusForward()) {
		    e.consume();
		}
		break;
	    }
	}
    }

    protected void paint(Graphics g) {
	paintBackground(g.createGraphics());
	paintWidget(g.createGraphics());
	paintChildren(g.createGraphics());
    }

    protected void paintBackground(Graphics g) {
	g.clearRect(0, 0, getWidth(), getHeight());
    }

    protected void paintWidget(Graphics g) {
    }

    protected void paintChildren(Graphics g) {
	Rectangle clipBounds = g.getClipBounds();

	Widget[] widgets = getWidgets(clipBounds);

	for (int i = 0; i < widgets.length; i++) {
	    Widget widget = widgets[i];

	    Graphics cg = g.createGraphics(widget.getX(), widget.getY(), widget
		    .getWidth(), widget.getHeight());
	    cg.setBackColor(widget.getBackColor());
	    cg.setColor(widget.getForeColor());
	    cg.setFont(widget.getFont());

	    widget.paint(cg);
	}
    }

    // TABに設定されているキーはここへはこない。
    protected void keyAction(KeyEvent event) {
	Assert.isNotNull(event);
    }

    protected void focusGained() {
    }

    protected void focusLost() {
    }

    // もし子（階層）にフォーカスがあるのならば移動させる。
    void focusInvalidate() {
	if (containsFocus()) {
	    RootControl root = getRootControl();
	    Widget focus = root.getFocus();
	    // System.out.println("a:" + focus);
	    if (!focus.transferFocusBackward() && !focus.transferFocusForward()) {
		root.setFocus(null);
	    }
	}
    }

    private class ChildWidgetListener implements WidgetListener {
	public void boundsChanged(Widget widget, int oldX, int oldY,
		int oldWidth, int oldHeight) {

	}

	public void widgetBoundChanged(Widget widget, Rectangle oldBounds) {
	    childBoundsChanged(widget, oldBounds.getX(), oldBounds.getY(),
		    oldBounds.getWidth(), oldBounds.getHeight());
	}

	public void widgetShown(org.gikolet.framework.ui.Widget c) {
	}

	public void widgetHidden(org.gikolet.framework.ui.Widget c) {
	}
    }
}
